#ifndef DATASTRUCTURE
#define DATASTRUCTURE

#include <QObject>
#include <QDateTime>


/*!
 \brief
 系统信息文件，结构定义参考：
 - 《山东省高速公路不停车收费项目卡片结构设计说明书》
 - 《GBT_28420-2012_电子收费_OBE-SAM数据格式和技术要求》
 - 《GBT_20851.4-2007_电子收费_专用短程通信_第4部分：设备应用》
*/
#pragma pack(push, 1)
struct RawObuSysInfoFile
{
    quint8 contractProvider[8];             /*!< 服务提供商名称 */
    quint8 contractType;                    /*!< 协约类型 */
    quint8 contractVersion;                 /*!< 合同版本 */
    quint8 contractSerialNumber[8];         /*!< 合同序列号 */
    quint8 contractSignedDate[4];           /*!< 合同签署日期 */
    quint8 contractExpiredDate[4];          /*!< 合同过期日期 */
    quint8 tamperStatus;                    /*!< 拆卸状态，按GBT_28420-2012，5.2.1，高4位0表示由路侧根据防拆信息控制OBU的通行，低4位为1表示正常工作状态，但已被拆过的电子标签低4位也为1 */
    quint8 vehicleLicencePlateNumber[12];   /*!< 车牌号 - 山东扩展 */
    quint8 vehicleLicencePlateColor[2];     /*!< 车牌颜色 - 山东扩展 */
    quint8 reserved[58];                    /*!< 预留 */
};
#pragma pack(pop)

/*!
 \brief
 OBU配置信息内容，长度为7字节，定义见：
 - 《GBT_28421-2012_电子收费_基于专用短程通信的电子收费交易》：有iccType的详细定义
 - 《GBT_20851.3-2007_电子收费_专用短程通信_第3部分_应用层》附录A.2。
*/
#pragma pack(push, 1)
struct RawObuConfiguration
{
    quint32 macID;                          /*!< MAC地址，同时作为LID */
    quint8  equipmentClassAndVersion;       /*!< 高4字节表示是否支持IC卡接口，0为不支持，1为支持IC卡 */
    quint8  obuStatus;                      /*!< 按位表示iccPresent(1), iccType(3), iccStatus(1), locked(1), tampered(1), battery(1)，0为正常 */
    quint8  reservedBits;
};
#pragma pack(pop)

class ObuInfo : public QObject
{
public:
    explicit ObuInfo(QObject *parent = 0);
    ObuInfo(const ObuInfo &obuInfo) : QObject(obuInfo.parent())
    {
        *this = obuInfo;
    }
    // 重载 '=' 号函数
    void operator =(const ObuInfo &obuInfo)
    {
        m_rawSysInfoFile=obuInfo.m_rawSysInfoFile;
        m_rawCfg=obuInfo.m_rawCfg;
        memcpy(&m_cipheredVehInfo, &obuInfo.m_cipheredVehInfo, sizeof(m_cipheredVehInfo));
        memcpy(&m_decipheredVehInfo, &obuInfo.m_decipheredVehInfo, sizeof(m_decipheredVehInfo));
        m_contractSignedDate=obuInfo.m_contractSignedDate;
        m_contractExpiredDate=obuInfo.m_contractExpiredDate;
        m_vehicleLicencePlateNumber=obuInfo.m_vehicleLicencePlateNumber;
        m_vehicleLicencePlateColor=obuInfo.m_vehicleLicencePlateColor;
    }

    bool isSame(const ObuInfo &another) const
    {
        return 0==memcmp(&m_rawSysInfoFile, &another.m_rawSysInfoFile, SYSFILE_SIZE);
    }

public:
    const static quint8 MIN_SYSFILE_SIZE=26;
    const static quint8 MAX_SYSFILE_SIZE=99;
    const static quint8 SYSFILE_SIZE=41;
    const static quint8 OBU_CFG_SIZE=7;
    const static quint8 VEH_INFO_FILE_SIZE=16;

public:

    /*!
     \brief
     设置OBU信息。
     \param sysInfo
     系统信息文件。
     \param sizeOfSysInfo
     系统信息文件长度。
     \param obuCfg
     OBU配置信息内容，长度为7字节。
     \return
     执行结果，true表示成功。
    */
    bool setObuInfo(const quint8 *sysInfo, quint8 sizeOfSysInfo, const quint8 *obuCfg);

    /*!
     * \brief setObuInfo测试辅助函数
     * \param sysInfo
     * \param obuCfg
     * \return 执行结果，true表示成功。
     */
    bool setObuInfo(const QString &sysInfo, const QString &obuCfg);
    /*!
     * \brief 保存加密车辆信息。
     * \param 加密车辆信息。
     * \param 加密数据长度。
     * \return
     */
    bool setCipheredVehInfoFile(const quint8 *vehInfoFile, quint8 fileLen);

//    bool setDecipheredVehInfoFile(const quint8 *vehInfoFile, quint8 fileLen);
    /*!
     * \brief 获取MAC地址。
     * \return MAC地址，转换为本地字节序。
     */
    quint32 getMacId() const;

    /*!
     * \brief 获取是否被拆卸。
     * \return true表示已被拆卸。
     */
    bool getTampered() const;

    /*!
     * \brief 获取是否已被锁。
     * \return true表示已被锁。
     */
    bool getLocked() const;

    /*!
     * \brief 获取是否插有可用的卡片，要求卡片已插入、状态正常、卡片类型被支持。
     * \return true表示有。
     */
    bool hasCpuCard() const;

    /*!
     * \brief 获取是否为山东发行的电子标签
     * \return true表示是。
     */
    bool isFromShandong() const;

    /*!
     * \brief 获取合同序列号。
     */
    QString getContractSN() const;

    /*!
     * \brief 获取合同签署日期。
     */
    QDate getSignedDate() const;

    /*!
     * \brief 获取合同过期日期。
     */
    QDate getExpiredDate() const;

    /*!
     * \brief 获取车牌号。
     */
    QString getVehicleLicencePlateNumber() const;

    /*!
     * \brief 获取带颜色的全车牌，如"蓝鲁A12345"。
     */
    QString getVehicleLicencePlateColorAndNumber() const;

    /*!
     * \brief 获取车牌颜色。
     */
    quint16 getVehicleLicencePlateColor() const;

protected:
    void setVehPlateNumber(quint8 *pPlate, int nLen);
    void setVehPlateColor(quint8 bHi, quint8 bLo);

protected:
    /*!
     * \brief 系统信息文件
     */
    RawObuSysInfoFile m_rawSysInfoFile;

    /*!
     * \brief OBU配置信息内容
     */
    RawObuConfiguration m_rawCfg;

    /*!
     * \brief 加密车辆信息文件
     */
    quint8 m_cipheredVehInfo[256];

    /*!
     * \brief 车辆信息文件
     */
    quint8 m_decipheredVehInfo[32];

    /*!
     * \brief 合同签署日期
     */
    QDate m_contractSignedDate;

    /*!
     * \brief 合同过期日期
     */
    QDate m_contractExpiredDate;

    /*!
     * \brief 车牌
     */
    QString m_vehicleLicencePlateNumber;

    /*!
     * \brief 车牌颜色(整型)
     */
    quint16 m_vehicleLicencePlateColor;

private:

};

Q_DECLARE_METATYPE(ObuInfo)

/*!
 \brief
 DF01卡片发行基本数据文件，结构定义参考：
 - 《收费公路联网收费技术要求》（未核对）
 - 《山东高速信联卡平台系统-信联卡卡片结构设计》
*/
#pragma pack(push, 1)
struct RawIccFile0015
{
    quint8 cardIssuer[8];                   /*!  8 发卡方标识，前4字节是省份汉字 */
    quint8 cardType;                        /*!  9 卡片类型，22-储值卡；23-记账卡 */
    quint8 cardVersion;                     /*! 10 卡片版本号 */
    quint8 cardNetwork[2];                  /*! 12 卡片网络编号，BCD编码，山东省厅发行的是3701，信联卡是3702 */
    quint8 cardNo[8];                       /*! 20 CPU卡片内部编号 */
    quint8 startTime[4];                    /*! 24 启用时间 */
    quint8 expireTime[4];                   /*! 28 到期时间 */
    quint8 vehicleLicencePlateNumber[12];   /*! 40 车牌号码 */
    quint8 userType;                        /*! 41 用户类型 */
    quint8 vehicleLicencePlateColor;        /*! 42 车牌颜色 - 国标为2字节整型，山东分出1个字节存储车型 */
    quint8 vehicleType;                     /*! 43 车型/鲁通C卡免费车种，低4位存车型，高4位存鲁通C卡免费车种 - 限山东 */
};
#pragma pack(pop)

/*!
 \brief
 DF02信联卡基本信息文件，结构定义参考：
 - 《山东高速信联卡平台系统-信联卡卡片结构设计》
*/
#pragma pack(push, 1)
struct RawIccXinlian0015
{
    quint8 cardIssuer[2];                   /*!  2 发卡方代码，BCD编码，填写运营商标识 */
    quint8 region[2];                       /*!  4 地域代码（城市代码），BCD编码，填写各地市区域代码，例如：山东3700 */
    quint8 industry[2];                     /*!  6 行业代码，BCD编码，预留 */
    quint8 rfu[2];                          /*!  8 RFU，预留 */
    quint8 appType;                         /*!  9 应用类型标识，1表示启用，默认填写0x01 */
    quint8 appVersion;                      /*! 10 应用版本，记录发行版本号，0x10代表支持复合消费，其它保留 */
    quint8 mutualFlag[2];                   /*! 12 互通标识（参与互通城市的标识），默认填写地域代码 */
    quint8 cardNo[8];                       /*! 20 CPU卡片内部编号（应用序列号），卡片应用ID，BCD编码 */
    quint8 startTime[4];                    /*! 24 启用时间 */
    quint8 expireTime[4];                   /*! 28 到期时间 */
    quint8 cardType;                        /*! 29 卡片主类型，普通储值卡-00；贷记卡（超级Ａ卡）-10 */
    quint8 subType;                         /*! 30 卡片子类型，默认填写 00 */
    quint8 scope[2];                        /*! 32 卡片应用范围代码，BCD，信联卡自己分配。分别对应某个或某一批商户。 */
    quint8 limitType;                       /*! 33 限制种类标志，0-无限制，1-包含在“使用限制信息文件”中的商户，才允许使用该卡。*/
    quint32 balanceLimit;                   /*! 37 电子钱包限额，单位为分 */
    quint8 reserved[13];                    /*! 50 保留 */
};
#pragma pack(pop)

/*!
 \brief
 DF02信联卡复合消费文件，结构定义参考：
 - 《山东高速信联卡平台系统-信联卡卡片结构设计》
*/
#pragma pack(push, 1)
struct RawIccXinlian0017
{
    quint8 flag;                            /*!  1 复合消费标志，09：全国通用消费 */
    quint8 dataLen;                         /*!  2 复合消费数据长度 */
    quint8 locked;                          /*!  3 复合消费锁定标志 */
    quint8 version;                         /*!  4 复合消费记录版本号(0x01) */
    quint8 transFlag;                       /*!  5 交易标识（0x00标识已完成） */
    quint8 termNo[6];                       /*! 11 终端机编号	*/
    quint32 moneyToDeductBigEndian;         /*! 15 金额（用于补扣） */
    quint8 deductTime[7];                   /*! 22 交易日期时间，BCD，YYYYMMDDHHMMSS */
    quint8 mac1[4];                         /*! 26 通用复合消费记录MAC */
    quint8 reserved[22];                    /*! 48 自定义数据	*/
};
#pragma pack(pop)

#pragma pack(push, 1)
struct RawIccInitForPurchaseRlt
{
    quint32 balanceBeforeInBigEndian;       /*!  4 扣款前余额 */
    quint16 transNoInBigEndian;             /*!  6 电子钱包脱机交易序号 */
    quint8 overdraftLimit[3];               /*!  9 透支限额 */
    quint8 keyVersion;                      /*! 10 密钥版本号 */
    quint8 algorithmId;                     /*! 11 算法标识 */
    quint8 random[4];                       /*! 15 随机数 */
};
#pragma pack(pop)


#pragma pack(push, 1)
struct RawIccMac1Rlt
{
    quint32 termTransNoInBigEndian;         /*!  4 终端脱机交易序号 */
    quint8 mac1[4];                         /*!  8 Mac1 */
};
#pragma pack(pop)


#pragma pack(push, 1)
struct RawIccTacRlt
{
    quint8 tac[4];                          /*!  4 TAC */
    quint8 mac2[4];                         /*!  8 Mac2 */
};
#pragma pack(pop)

class IccInfo : public QObject
{
public:
    explicit IccInfo(QObject *parent = 0);
    IccInfo(const IccInfo &iccInfo) : QObject(iccInfo.parent())
    {
        *this = iccInfo;
    }

    void operator =(const IccInfo &iccInfo)
    {
        m_rawIccFile0015=iccInfo.m_rawIccFile0015;
        m_rawIccXinlian0015=iccInfo.m_rawIccXinlian0015;
        m_rawIccInitForPurchaseRlt=iccInfo.m_rawIccInitForPurchaseRlt;
        m_rawIccMac1Rlt=iccInfo.m_rawIccMac1Rlt;
        m_rawIccTacRlt=iccInfo.m_rawIccTacRlt;
        memcpy(&m_xinlianTermNo, &iccInfo.m_xinlianTermNo, sizeof(m_xinlianTermNo));
        memcpy(&m_xinlianPsamId, &iccInfo.m_xinlianPsamId, sizeof(m_xinlianPsamId));
        m_nGbBalance=iccInfo.m_nGbBalance;
        m_nXinlianBalance=iccInfo.m_nXinlianBalance;
        m_startTime=iccInfo.m_startTime;
        m_expireTime=iccInfo.m_expireTime;
        m_nMoneyToDeduct=iccInfo.m_nMoneyToDeduct;
        m_deductTime=iccInfo.m_deductTime;
        m_isXinlianCard=iccInfo.m_isXinlianCard;
    }

    QString toString() const;

public:
    // 国标0015文件长度
    const static quint8 DF01_0015_SIZE=43;
    // 国标余额长度
    const static quint8 DF01_BALANCE_SIZE=4;

    // 信联0015文件长度
    const static quint8 DF02_0015_SIZE=50;

public:
    /*!
     \brief
     设置DF01卡片发行基本数据文件，更新此文件时将清空信联卡相关文件。
     \param icc0015
     DF01卡片发行基本数据文件。
     \param sizeOfIcc0015
     DF01卡片发行基本数据文件长度。
     \return
     执行结果，true表示成功。
    */
    bool setIcc0015(const quint8 *icc0015, quint8 sizeOfIcc0015);
    /*!
     * \brief setIcc0015
     * \param icc0015
     * \return 执行结果，true表示成功。
     */
    bool setIcc0015(const QString &icc0015);

    bool setGbBalance(const quint8 *rawBalance, quint8 sizeOfBalance);

    bool setXinlianBaseInfo(const quint8 *xinlian0015, quint8 sizeOfXinlian0015);
    bool setXinlianBaseInfo(const QString &xinlian0015);

    bool setXinlianBalance(const quint8 *rawBalance);
    bool setXinlianBalance(const QString &balance);

    bool setXinlianInfoForPurchase(const quint8 *initPurchaseRlt);
    bool setXinlianInfoForPurchase(const QString &initPurchaseRlt);

    bool setXinlianMac1(const quint8 *mac1Rlt);
    bool setXinlianMac1(const QString &mac1Rlt);

    bool setXinlianTac(const quint8 *tacRlt);
    bool setXinlianTac(const QString &tacRlt);

    bool setXinlianTacFromGetTransactionProve(const quint8 *tacRlt);
    bool setXinlianTacFromGetTransactionProve(const QString &tacRlt);

    /*!
     * \brief 设置终端机编号
     * \param 终端机编号
     */
    bool setTermNo(const quint8 *termNo)
    {
        memcpy(m_xinlianTermNo, termNo, 6);
        return true;
    }
    bool setTermNo(const QString &termNo);

    bool setPsamId(const quint8 *psamId)
    {
        memcpy(m_xinlianPsamId, psamId, 10);
        return true;
    }

    /*!
     * \brief 设置扣款信息
     * \param 扣款金额
     * \param 扣款时间
     * \return
     */
    bool setDeductInfo(quint32 nMoneyToDeduct, QDateTime deductTime);

    /*!
     * \brief MAC1 计算初始化，Init Sam For Purchase。参《交通部PSAM卡参考手册V105》。
     * \return
     */
    quint8 *getInitSamForPurchaseCmd() const;

    /*!
     * \brief 写记录文件，UPDATE RECORD。用于更新线性定长结构的文件记录。参《交通部PSAM卡参考手册V105》。
     * \return
     */
    quint8 *getUpdateRecord0017Cmd() const;

    /*!
     * \brief 消费命令，DEBIT FOR CAPP PURCHASE。
     * \return
     */
    quint8 *getDebitForCappPurchaseCmd() const;

    /*!
     * \brief 校验MAC2命令，CREDIT_SAM_FOR_PURCHASE。参《交通部PSAM卡参考手册V105》。
     * \return
     */
    quint8 *getCreditSamForPurchaseCmd() const;

    /*!
     * \brief 取交易认证，Get Transaction Prove。参《SmartCos用户手册3.3》。
     * \return
     */
    quint8 *getTransactionProveCmd() const;

    /*!
     * \brief 判断信联卡是否在有效期内。
     * \return true表示有效。
     */
    bool isDateValid();

    /*!
     * \brief 获取启用时间。
     */
    inline QDate getStartTime()
    {
        return this->m_startTime;
    }

    /*!
     * \brief 获取到期时间。
     */
    QDate getExpireTime()
    {
        return this->m_expireTime;
    }

    /*!
     * \brief 获取是否为山东发行的卡片
     * \return true表示是。
     */
    inline bool isFromShandong()
    {
        // "\xc9\xbd\xb6\xab": "山东"的GB2312编码
        return 0==memcmp("\xc9\xbd\xb6\xab", this->m_rawIccFile0015.cardIssuer, 4);
    }

    /*!
     * \brief 根据icc0015内的发卡方标识和卡片网络编号判断卡片是否为信联卡。
     * \return true表示是。
     */
    inline bool isXinlianCardBasedOnGB()
    {
        return (0x37==this->m_rawIccFile0015.cardNetwork[0]) &&
               (0x02==this->m_rawIccFile0015.cardNetwork[1]) &&
               this->isFromShandong();
    }

    inline void setIsXinlianCard(bool isXinlianCard)
    {
        this->m_isXinlianCard = isXinlianCard;
    }

    inline bool getIsXinlianCard() const
    {
        return this->m_isXinlianCard;
    }

    /*!
     * \brief 获取信联卡电子钱包余额。
     */
    inline quint32 getXinlianBalance() const
    {
        return this->m_nXinlianBalance;
    }

    inline quint32 getGbBalance() const
    {
        return this->m_nGbBalance;
    }

    /*!
     * \brief 获取网络编号（4位）
     */
    QString getGBNetworkNo() const;

    /*!
     * \brief 获取国标卡类型
     */
    int getGBCardType() const
    {
        return this->m_rawIccFile0015.cardType;
    }

    /*!
     * \brief 获取国标卡号（16位）
     */
    QString getGBCardNo() const;

    /*!
     * \brief 获取国标卡号（20位，含网络编号）
     */
    QString getGBCardNoWithNetworkNo() const;

    /*!
     * \brief 获取国标15文件中的车牌号
     */
    QString getVehicleLicencePlateNumber() const;

    /*!
     * \brief 获取国标15文件中的车牌颜色
     */
    int getVehicleLicencePlateColor() const;

    /*!
     * \brief 获取信联卡卡号（16位）
     */
    QString getXinlianCardNo() const;

protected:
    /*!
     * \brief DF01卡片发行基本数据文件，用于判断是否为信联卡、辅助判别车型（车牌与OBU内一致时可以此车型从卡内扣款）。
     */
    RawIccFile0015 m_rawIccFile0015;

    quint32 m_nGbBalance;

    /*!
     \brief
     DF02信联卡基本信息文件。
    */
    RawIccXinlian0015 m_rawIccXinlian0015;

    RawIccInitForPurchaseRlt m_rawIccInitForPurchaseRlt;

    RawIccMac1Rlt m_rawIccMac1Rlt;

    RawIccTacRlt m_rawIccTacRlt;

    /*!
     * \brief 终端机编号
     */
    quint8 m_xinlianTermNo[6];

    quint8 m_xinlianPsamId[10];

    /*!
     \brief
     信联卡电子钱包余额。
    */
    quint32 m_nXinlianBalance;

    /*!
     * \brief 启用时间，以信联卡基本信息文件为准。
     */
    QDate m_startTime;

    /*!
     * \brief 到期时间，以信联卡基本信息文件为准。
     */
    QDate m_expireTime;

    /*!
     * \brief 扣款金额
     */
    quint32 m_nMoneyToDeduct;

    /*!
     * \brief 扣款时间
     */
    QDateTime m_deductTime;

    /*!
     * \brief 是否为信联卡，执行消费初始化后有效。
     */
    bool m_isXinlianCard;
};

Q_DECLARE_METATYPE(IccInfo)

#endif // DATASTRUCTURE

